package org.orion.engine.demo;

import java.awt.AWTException;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.nio.FloatBuffer;
import java.util.Iterator;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;
import javax.swing.Timer;

import org.orion.engine.CameraFly;
import org.orion.engine.FrameBuffer;
import org.orion.engine.GLStatic;
import org.orion.engine.Mesh;
import org.orion.engine.Program;
import org.orion.engine.RenderBuffer;
import org.orion.engine.Texture;
import org.orion.engine.TextureExt;
import org.orion.engine.VertexBuffer;
import org.orion.engine.math.Vector2i;
import org.orion.engine.math.Vector3d;
import org.orion.engine.math.Vector4i;
import org.orion.engine.math.VectorUtils;
import org.orion.engine.util.TestMesh;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.BufferUtil;


public class Main extends JFrame implements GLEventListener,MouseMotionListener,KeyListener,ActionListener {
	
	private long countFrame = 0;
	
	private GL gl = null;
	private GLU glu = new GLU();
	private GLCanvas canvas = null;
	private CameraFly camera = new CameraFly(); 
	public Vector2i mousePoint = new Vector2i();
	public boolean mouseEnable = true;
	
	ScreenMode screenMode = null;
	
	private Mesh[] meshes = new Mesh[6];
	private Texture[] textures = new TextureExt[3];
	private FrameBuffer frameBuffer;
	private Program program;
	private Program programFbo;
	private Texture textureFbo;
	
	
	
	private static String vertexShader = "data/shaders/texture.vert";
	private static String fragmentShader = "data/shaders/texture.frag";
	
	private static String vertexShaderFbo = "data/shaders/fbo.vert";
	private static String fragmentShaderFbo = "data/shaders/fbo.frag";
	
	
	
	public Main() {
		// initialize the GLCanvas
		canvas = new GLCanvas();
		canvas.addGLEventListener(this);
		canvas.addMouseMotionListener(this);
		canvas.addKeyListener(this);
		
		getContentPane().add(canvas, java.awt.BorderLayout.CENTER);
		setSize(800, 600);
		setTitle("OpenGL");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
		canvas.requestFocusInWindow();
		canvas.setCursor(null);
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Image image = toolkit.createImage(new byte[]{0});
		Cursor noCursor = toolkit.createCustomCursor(image, new Point(1,1),
		                                             "blank cursor");
		canvas.setCursor(noCursor);
		
		screenMode = new ScreenMode(this, new DisplayMode (1280, 1024, 32, 75));
		//screenMode.setFullScreenMode();
		// the Animator is used to constantly update the screen
		Animator animator = new Animator(canvas);
		animator.start();
		
		Timer t = new Timer(1000,this);
		t.start();
		
	}
	
	
	public void init(GLAutoDrawable drawable) {
		gl = drawable.getGL();
		GLStatic.gl = gl;
		GLStatic.glu = new GLU();
		
		gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glEnable(GL.GL_LIGHTING);
		gl.glEnable(GL.GL_LIGHT0);
		
		textures[0] = new TextureExt(GL.GL_TEXTURE_2D, "data/test.tga");
		textures[1] = new TextureExt(GL.GL_TEXTURE_2D, "data/cratexa.png");
		textures[2] = new TextureExt(GL.GL_TEXTURE_2D, "data/testa.png");
		
		textureFbo = new Texture(GL.GL_TEXTURE_2D);
		textureFbo.glBind();
		gl.glTexImage2D( textureFbo.getType(), 0, GL.GL_RGBA, 
		          256, 256, 
		          0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, null );
		gl.glTexParameteri( GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR );
		gl.glTexParameteri( GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR );
		
		//gl.glActiveTexture(GL.GL_TEXTURE1);
		//gl.glBindTexture(GL.GL_TEXTURE_2D, textures[1].getId());
		//gl.glUniform1i(gl.glGetUniformLocation( program.getId(), "texture0"), 0);
		//gl.glUniform1i(gl.glGetUniformLocation( program.getId(), "texture1"), 1);
		
		meshes[0] = new VertexBuffer( TestMesh.traiangles().getType(), TestMesh.traiangles().getData() );
		meshes[0].setPosition(new Vector3d(0.0, 0.0, -5.0));
		meshes[1] = new Mesh( TestMesh.quad().getType(), TestMesh.quad().getData() );
		meshes[2] = new Mesh( TestMesh.quad().getType(), TestMesh.quad().getData() );
		meshes[2].setPosition(new Vector3d(0.0, -5.0, 0.0));
		
		meshes[3] = new Mesh( TestMesh.quad().getType(), TestMesh.quad().getData() );
		meshes[3].setPosition(new Vector3d(-5.0, 0.0, 0.0));
		meshes[3].setTexture(textures[1]);
		
		meshes[4] = new Mesh( TestMesh.axis().getType(), TestMesh.axis().getMode(), TestMesh.axis().getData() );
		meshes[4].setScale(new Vector3d(5.0, 5.0, 5.0));
		
		meshes[5] = new Mesh( TestMesh.cub().getType(),TestMesh.cub().getData() );
		meshes[5].setPosition(new Vector3d(0.0, 5.0, 0.0));
		meshes[5].setTexture(textureFbo);
		
		program = new Program(vertexShader, fragmentShader);
		program.glLink();
		program.glUse();
		
		programFbo = new Program(vertexShaderFbo, fragmentShaderFbo);
		programFbo.glLink();
		
		frameBuffer = new FrameBuffer(256, 256);
		frameBuffer.glBind();
		
		//RenderBuffer renderBufferDepth = new RenderBuffer(GL.GL_DEPTH_COMPONENT24, 
		//		frameBuffer.getWidth(), frameBuffer.getHeight());
		
		frameBuffer.glAttach(GL.GL_DEPTH_ATTACHMENT_EXT, new RenderBuffer(GL.GL_DEPTH_COMPONENT24, 
				frameBuffer.getWidth(), frameBuffer.getHeight()));
		frameBuffer.glAttach(GL.GL_COLOR_ATTACHMENT0_EXT, textureFbo, 0);
		frameBuffer.checkStatus();		
		FrameBuffer.glUnBind();
		
		gl.setSwapInterval(0);
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA );
		//gl.glDepthMask(false);
		
	}
	
	
	
	public void display(GLAutoDrawable drawable) {
		
		
		frameBuffer.glBind();
		gl.glViewport( 0, 0, frameBuffer.getWidth(), frameBuffer.getHeight() );
		gl.glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
	
		gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
		
		camera.glModelView();
		camera.glProjection();
		
		for (Mesh mesh : meshes) {
			if (mesh!=null){
				mesh.draw();
			}
		}
	    
		FrameBuffer.glUnBind();
		/*
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
	    glu.gluOrtho2D(0.0, 0.0, 256, 256);
	    
	    gl.glBegin(GL.GL_QUADS);
	    	gl.glVertex2d(0.0, 0.0);
	    	gl.glVertex2d(0.0, 1.0);
	    	gl.glVertex2d(1.0, 0.0);
	    	gl.glVertex2d(1.0, 1.0);
	    gl.glEnd();
		
		*/
		
		camera.glViewport();
		gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		program.glUse();
		
		camera.glModelView();
		camera.glProjection();
		
		for (Mesh mesh : meshes) {
			if (mesh!=null){
				mesh.draw();
			}
		}
		countFrame++;
		
	}
	
	
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
		// do nothing
	}
	
	
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		camera.setViewport(new Vector4i(0, 0, width, height));
		camera.glViewport();
		camera.glProjection();
	}
	
	
	
	
	
	public static void main(String[] args) {
		new Main();
	}
	
	public void keyPressed(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}


	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}


	public void keyTyped(KeyEvent e) {
		switch ( Character.toLowerCase(e.getKeyChar()) )
		{
			case 'a':
			 camera.left(); break;
			case 'd':
				camera.right(); break;
			case 'w':
				camera.forward(); break;
			case 's':
				camera.back(); break;
			case ' ':
				camera.position.zero();  break;
			case 'q':
				camera.sizeStep--; break;
			case 'e':
				camera.sizeStep++; break;
		}
		canvas.repaint();
		
	}
	
	public void mouseDragged(java.awt.event.MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}


	public void mouseMoved(java.awt.event.MouseEvent e) {
		if (!mouseEnable){
			mousePoint.set(e.getX(),e.getY()) ;
			mouseEnable = true;
			return;
		}
		
		if ( e.getModifiers()==MouseEvent.SHIFT_MASK && mouseEnable){
			camera.rotateLeftRight(e.getX()-mousePoint.x);
			camera.rotateUpDown(e.getY()-mousePoint.y);
			mousePoint.set(e.getX(),e.getY()) ;
			mouseEnable = false;
			try {
				(new Robot()).mouseMove(getBounds().x+getBounds().width/2,
										getBounds().y+getBounds().height/2);
			} catch (AWTException e1) {
				
			}
		}
		
		canvas.repaint();
	}
	
	

	public void actionPerformed(ActionEvent arg0) {
		this.setTitle("FPS="+countFrame);
		countFrame=0;
	}
	
}

